<?php
namespace Extend\Lib;

/**
 * css/js 压缩扩展
 * Class CompressFile
 * @package Extend\Lib
 * @auther: 李海波 <lihaibo123as@163.com>
 */
class CompressFile {

    public static $cacheJs = array();//js 队列
    public static $cacheCss = array();//css 队列
    private static $ins = null; //实例化
    private static $startIime = array();//时间统计

    /**
     * 获取单例
     * @return CompressFile|null
     */
    public static function getIns() {
        if (self::$ins instanceof CompressFile) {
            return self::$ins;
        } else {
            self::$ins = new CompressFile();
            return self::$ins;
        }
    }

    /**
     * 放入队列
     * @param $key
     * @param $file
     * @param bool $flag
     * @return string
     */
    public static function put($key, $file, $flag = false) {
        $file = trim($file, '/');
        if (file_exists($file)) {
            switch (self::checkFileAuth($file)) {
                case "css":
                    self::$startIime['css'] ?: self::$startIime['css'] = microtime(true);
                    is_array(self::$cacheCss[$key]) ?: self::$cacheCss[$key] = array();
                    if (!in_array($file, self::$cacheCss[$key])) {
                        $filehash = hash_file('crc32', $file);
                        self::$cacheCss[$key][$filehash] = $file;
                    }
                    return $flag ? self::out('css', $key) : '';
                    break;
                case "js":
                    self::$startIime['js'] ?: self::$startIime['js'] = microtime(true);
                    is_array(self::$cacheJs[$key]) ?: self::$cacheJs[$key] = array();
                    if (!in_array($file, self::$cacheJs[$key])) {
                        $filehash = hash_file('crc32', $file);
                        self::$cacheJs[$key][$filehash] = $file;
                    }
                    return $flag ? self::out('js', $key) : '';
                    break;
            }
        }
    }

    /**
     * 输出
     * @param $type
     * @param $key
     * @return string
     */
    public static function out($type, $key) {
        switch ($type) {
            case "css":
                if (APP_DEBUG || C('CONFIG_MIN_FILE') != 1) {
                    $content = '';
                    if (!empty(self::$cacheCss[$key])) {
                        foreach (self::$cacheCss[$key] as $k => $v) {
                            $content .= \Extend\Lib\PublicTool::buildCssLink('/'.$v . '?hash=' . $k);
                        }
                    }
                    return $content;
                }
                $cachePath = RUNTIME_PATH . 'Application/Static/css/';
                if (!is_dir($cachePath)) {
                    \Extend\Lib\PublicTool::mkdirs($cachePath);
                }
                $cacheUrl = __ROOT__ . '/Runtime/Application/Static/css/';
                $md5Name = $key . '-' . md5(json_encode(self::$cacheCss[$key])) . '.css';
                $cacheFile = $cachePath . $md5Name;
                if (!file_exists($cacheFile) && !empty(self::$cacheCss[$key])) {
                    $file_note = '/*' . PHP_EOL . '%s' . PHP_EOL . 'Cache Time:' . date("Y-m-d H:i:s") . PHP_EOL . '*/' . PHP_EOL;
                    $allContent = '';
                    $allContent .= '@charset "utf-8";' . PHP_EOL;
                    foreach (self::$cacheCss[$key] as $k => $v) {
                        $content = file_get_contents($v);
                        $content = self::minCss($content);
                        $content = self::complateImageUrl($content, $v);
                        $allContent .= $content;
                    }
                    $allContent = sprintf($file_note, $cacheUrl . $md5Name) . $allContent;
                    file_put_contents($cacheFile, $allContent);
                    trace("min css file create:" . $cacheFile . ' costTime:' . (microtime(true) - self::$startIime[$type]));
                }
                trace("load css file:" . $cacheFile . ' costTime:' . (microtime(true) - self::$startIime[$type]));
                return \Extend\Lib\PublicTool::buildCssLink($cacheUrl . $md5Name);
                break;
            case "js":
                if (APP_DEBUG || C('CONFIG_MIN_FILE') != 1) {
                    $content = '';
                    if (!empty(self::$cacheJs[$key])) {
                        foreach (self::$cacheJs[$key] as $k => $v) {
                            $isExist = strpos($v,"Public");
                            if ($isExist == 0){
                                $content .= \Extend\Lib\PublicTool::buildJsLink('/'.$v . '?hash=' . $k) . PHP_EOL;
                            } else{
                                $content .= \Extend\Lib\PublicTool::buildJsLink($v . '?hash=' . $k) . PHP_EOL;
                            }
                        }
                    }
                    return $content;
                }
                $cachePath = RUNTIME_PATH . 'Application/Static/scripts/';
                if (!is_dir($cachePath)) {
                    \Extend\Lib\PublicTool::mkdirs($cachePath);
                }
                $cacheUrl = __ROOT__ . '/Runtime/Application/Static/scripts/';
                $md5Name = $key . '-' . md5(json_encode(self::$cacheJs[$key])) . '.js';
                $cacheFile = $cachePath . $md5Name;
                if (!file_exists($cacheFile) && !empty(self::$cacheJs[$key])) {
                    $file_note = '/*' . PHP_EOL . '%s' . PHP_EOL . 'Cache Time:' . date("Y-m-d H:i:s") . PHP_EOL . '*/' . PHP_EOL;
                    $allContent = '';
                    foreach (self::$cacheJs[$key] as $k => $v) {
                        $content = file_get_contents($v);
                        $content = self::minJS($content);
                        $allContent .= $content;
                    }
                    $allContent = sprintf($file_note, $cacheUrl . $md5Name) . $allContent;
                    file_put_contents($cacheFile, $allContent);
                    trace("min js file create:" . $cacheFile . ' costTime:' . (microtime(true) - self::$startIime[$type]));
                }
                trace("load js file:" . $cacheFile . ' costTime:' . (microtime(true) - self::$startIime[$type]));
                return \Extend\Lib\PublicTool::buildJsLink($cacheUrl . $md5Name);
                break;
        }
    }

    //允许压缩文件类型
    public static $content_type_arr = array(
        'css' => "text/css",
        'js' => "application/javascript"
    );

    /**
     * 压缩css
     * @param type $content
     * @return type
     */
    private static function minCss($content) {
        $content = preg_replace('/^@charset(.*)?;/', '', $content); // {//注释情况特殊处理
        $content = preg_replace('!/\*[^*]*\*+([^/][^*]*\*+)*/!', '', $content);
        $content = str_replace(array("\r\n", "\r", "\n", "\t", '  ', '    ', '    '), '', $content);
        return $content;
    }

    private static function complateImageUrl($content, $file) {
        $file_arr = explode("/", $file);
        unset($file_arr[count($file_arr) - 1]);
        preg_match_all("/url\([\"\']?(.*?)[\"\']?\)/", $content, $matchs);
        $urls = $matchs[1];
        $replace_urls = array();
        if (!empty($urls)) {
            foreach ($urls as $key => $k) {
                $k_arr = explode("/", $k);
                $temp_k_arr = $file_arr;
                if (0 === strpos($k, 'data:')) {//查找首字母 data:
                    $replace_urls[$key] = $matchs[0][$key];
                    continue;
                }
                if (0 === strpos($k, 'http:')) {//查找首字母 http:
                    $replace_urls[$key] = $matchs[0][$key];
                    continue;
                }
                foreach ($k_arr as $kk => $vv) {
                    if ($vv == '..' && count($temp_k_arr) > 0) {
                        unset($temp_k_arr[count($temp_k_arr) - 1]);
                    } else {
                        $temp_k_arr[] = $vv;
                    }
                }
                $replace_urls[$key] = str_replace($k, \Extend\Lib\PublicTool::complateUrl(implode("/", $temp_k_arr)), $matchs[0][$key]);
            }
            $content = str_replace($matchs[0], $replace_urls, $content);
        }
        return $content;
    }

    /**
     * 压缩js
     * @param type $content
     * @return type
     */
    private static function minJS($content) {
        return \Extend\Lib\JSMin::minify($content);
    }

    /**
     * 验证文件
     * @param type $filename
     * @return boolean
     */
    public static function checkFileAuth($filename) {
        $type = self::fixFileType($filename);
        if (!array_key_exists($type, self::$content_type_arr)) {
            return false;
        }
        return $type;
    }

    /**
     * 获取文件类型后缀
     * @param type $filename
     * @return boolean
     */
    protected static function fixFileType($filename) {
        preg_match_all('/\.(([a-zA-Z]+)?)/', $filename, $match);
        $length = count($match);
        $type = $match[$length - 1][count($match[$length - 1]) - 1];
        if (empty($match) || !array_key_exists($type, self::$content_type_arr)) {
            return FALSE;
        }
        return $type;
    }
}
